/*
    Licensed to the Apache Software Foundation (ASF) under one
    or more contributor license agreements.  See the NOTICE file
    distributed with this work for additional information
    regarding copyright ownership.  The ASF licenses this file
    to you under the Apache License, Version 2.0 (the
    "License"); you may not use this file except in compliance
    with the License.  You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing,
    software distributed under the License is distributed on an
    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    KIND, either express or implied.  See the License for the
    specific language governing permissions and limitations
    under the License.
 */
package erwiki.api.pages;

import java.util.Collection;
import java.util.List;
import java.util.Set;

import org.elwiki.IWikiConstants;
import org.elwiki_data.AttachmentContent;
import org.elwiki_data.PageAttachment;
import org.elwiki_data.PageContent;
import org.elwiki_data.PageReference;
import org.elwiki_data.UnknownPage;
import org.elwiki_data.WikiPage;

import erwiki.api.authorization.IGroupWiki;
import erwiki.api.component.IPartPreferences;
import erwiki.api.core.WikiContext;
import erwiki.api.exceptions.ProviderException;
import erwiki.api.exceptions.WikiException;
import erwiki.api.providers.PageProvider;

public interface PageManager extends IPartPreferences {

	interface Prefs {
		String PAGE_PROVIDER = "pageProvider";

		/** If this property is set to false, we don't allow the creation of empty pages. */
		String ALLOW_CREATION_OF_EMPTY_PAGES = "allowCreationOfEmptyPages";
	}

	/** Defines type of page motion. */
	enum PageMotionType {
		AFTER, BEFORE, BOTTOM
	};

	/** The property value for setting the current page provider. Value is {@value}. */
	@Deprecated
	String PROP_PAGEPROVIDER = "jspwiki.pageProvider";

	/**
	 * The property value for setting the amount of time before the page locks expire. Value is
	 * {@value}.
	 */
	String PROP_LOCKEXPIRY = "jspwiki.lockExpiryTime";

	/**
	 * Возвращает экземпляр поставщика страниц, используемый в данный момент.
	 *
	 * @return Экземпляр PageProvider.
	 */
	PageProvider getCurrentProvider();

	/**
	 * Возвращает только страницы верхнего уровня.
	 *
	 * @return Коллекция WikiPage верхнего уровня.
	 * @throws ProviderException если что-то пошло не так, как ожидалось.
	 */
	Collection<WikiPage> getUpperPages() throws ProviderException;

	/**
	 * Возвращает коллекцию всех страниц Wiki, в случайном порядке. Этот метод может вызвать не
	 * эффективный доступ к хранилищу.</br>
	 * Если требуются только названия страниц, см. {@link Id2NamePage#getAllPageNames()}, что, вероятно,
	 * намного быстрее.
	 *
	 * @return Коллекция экземпляров WikiPage.
	 * @throws ProviderException если что-то пошло не так, как ожидалось.
	 */
	Collection<WikiPage> getAllPages() throws ProviderException;

	/**
	 * Fetches the page text from the repository. This method also does some sanity checks, like
	 * checking for the pageName validity, etc. Also, if the page repository has been modified
	 * externally, it is smart enough to handle such occurrences.
	 *
	 * @param pageName The name of the page to fetch.
	 * @param version  The version to find
	 * @return The page content as a raw string
	 * @throws ProviderException если что-то пошло не так, как ожидалось.
	 */
	String getPageText(String pageName, int version) throws ProviderException;

	/**
	 * Returns the pure text of a page, no conversions. Use this if you are writing something that
	 * depends on the parsing of the page. Note that you should always check for page existence through
	 * pageExists() before attempting to fetch the page contents.
	 *
	 * This method is pretty similar to {@link #getPageText(String, int)}, except that it doesn't throw
	 * {@link ProviderException}, it logs and swallows them.
	 *
	 * @param page    The name of the page to fetch.
	 * @param version If WikiPageProvider.LATEST_VERSION, then uses the latest version.
	 * @return The page contents. If the page does not exist, returns an empty string.
	 */
	String getPureText(String page, int version);

	String getText(String pageName, int version);

	/**
	 * Returns the un-HTMLized text of the latest version of a page. This method also replaces the &lt;
	 * and &amp; -characters with their respective HTML entities, thus making it suitable for inclusion
	 * on an HTML page. If you want to have the page text without any conversions, use
	 * {@link #getPureText(String, int)}.
	 *
	 * @param page WikiName of the page to fetch.
	 * @return WikiText.
	 */
	default String getText(final String page) {
		return getText(page, IWikiConstants.LATEST_VERSION);
	}

	/**
	 * Writes the WikiText of a page into the page repository. If the <code>preferences.ini</code> file
	 * contains the property <code>jspwiki.approver.workflow.saveWikiPage</code> and its value resolves
	 * to a valid user, {@link IGroupWiki} or {@link org.elwiki.data.authorize.GroupPrincipal}, this
	 * method will place a {@link org.apache.wiki.workflow.IDecision} in the approver's workflow inbox
	 * and throw a {@link org.apache.wiki.workflow.DecisionRequiredException}. If the submitting user is
	 * authenticated and the page save is rejected, a notification will be placed in the user's decision
	 * queue.
	 *
	 * @param text The Wiki markup for the page.
	 * @throws WikiException if the save operation encounters an error during the save operation. If the
	 *                       page-save operation requires approval, the exception will be of type
	 *                       {@link org.apache.wiki.workflow.DecisionRequiredException}. Individual
	 *                       PageFilters, such as the {@link org.apache.wiki.filters.filters.SpamFilter}
	 *                       may also throw a {@link org.apache.wiki.api.exceptions.RedirectException}.
	 */
	void saveText(String text, String author, String changenote) throws WikiException;

	/**
	 * Writes the comment text of a page into the page repository.
	 *
	 * @param wikiContext The current WikiContext.
	 * @param comment     The Wiki markup for the comment.
	 * @throws WikiException если что-то пошло не так, как ожидалось.
	 */
	void savePageComment(WikiContext wikiContext, String comment) throws WikiException;

	/**
	 * Помещает текст в заданную страницу.
	 *
	 * @param page       WikiPage, в которой нужно сохранить новый текст.
	 * @param text       Текст для сохранения.
	 * @param author     Автор, вносящий новый текст.
	 * @param changenote Комментарий для нового текста.
	 * @throws ProviderException если что-то пошло не так, как ожидалось.
	 */
	void putPageText(WikiPage page, String text, String author, String changenote) throws ProviderException;

	/**
	 * Locks page for editing. Note, however, that the PageManager will in no way prevent you from
	 * actually editing this page; the lock is just for information.
	 *
	 * @param page WikiPage to lock
	 * @param user Username to use for locking
	 * @return null, if page could not be locked.
	 */
	PageLock lockPage(WikiPage page, String user);

	/**
	 * Marks a page free to be written again. If there has not been a lock, will fail quietly.
	 *
	 * @param lock A lock acquired in lockPage(). Safe to be null.
	 */
	void unlockPage(PageLock lock);

	/**
	 * Returns the current lock owner of a page. If the page is not locked, will return null.
	 *
	 * @param page The page to check the lock for
	 * @return Current lock, or null, if there is no lock
	 */
	PageLock getCurrentLock(WikiPage page);

	/**
	 * Returns a list of currently applicable locks. Note that by the time you get the list, the locks
	 * may have already expired, so use this only for informational purposes.
	 *
	 * @return List of PageLock objects, detailing the locks. If no locks exist, returns an empty list.
	 */
	List<PageLock> getActiveLocks();

	/**
	 * Находит соответствующий объект WikiPage, по имени страницы.
	 *
	 * @param pageName Имя страницы, которую нужно найти.
	 * @return Экземпляр WikiPage или значение <code>null</code>, если требуемая страница не найдена.
	 */
	WikiPage getPageByName(String pageName);

	/**
	 * Finds the corresponding WikiPage object base on the page name and version.
	 *
	 * @param pagereq The name of the page to look for.
	 * @param version The version number to look for. May be WikiProvider.LATEST_VERSION, in which case
	 *                it will look for the latest version (and this method then becomes the equivalent
	 *                of getPage(String).
	 *
	 * @return A WikiPage object, or null, if the page could not be found; or if there is no such
	 *         version of the page.
	 */
	WikiPage getPage(String pagereq, int version);

	/**
	 * Finds a WikiPage object describing a particular page and version.
	 *
	 * @param pageName The name of the page
	 * @param version  A version number
	 * @return A WikiPage object, or null, if the page does not exist
	 * @throws ProviderException если что-то пошло не так, как ожидалось.
	 */
	WikiPage getPageInfo(String pageName, int version) throws ProviderException;

	/**
	 * Gets a version history of page. Each element in the returned List is a WikiPage.
	 *
	 * @param pageName The name of the page or attachment to fetch history for
	 * @return If the page does not exist or there's some problem retrieving the version history,
	 *         returns null, otherwise a List of WikiPages / Attachments, each corresponding to a
	 *         different revision of the page / attachment.
	 */
	@Deprecated
	List<PageContent> getVersionHistory(WikiPage page);

	/**
	 * Returns a human-readable description of the current provider.
	 *
	 * @return A human-readable description.
	 */
	String getProviderDescription();

	/**
	 * Returns the total count of all pages in the repository. This method is equivalent of calling
	 * getAllPages().size(), but it swallows the ProviderException and returns -1 instead of any
	 * problems.
	 *
	 * @see org.apache.wiki.pages0.PageManager#getTotalPageCount()
	 *
	 * @return The number of pages, or -1, if there is an error.
	 */
	int getTotalPageCount();

	/**
	 * Returns a Collection of WikiPages, sorted in time order of last change (i.e. first object is the
	 * most recently changed). This method also includes attachments.
	 *
	 * @return Set of WikiPage objects.
	 */
	Set<WikiPage> getRecentChanges();

	/**
	 * Returns true, if the page exists (any version) on the underlying WikiPageProvider.
	 *
	 * @param pageName Name of the page.
	 * @return A boolean value describing the existence of a page
	 * @throws ProviderException если что-то пошло не так, как ожидалось.
	 */
	boolean pageExists(String pageName) throws ProviderException;

	/**
	 * Checks for existence of a specific page and version on the underlying WikiPageProvider.
	 *
	 * @param pageName Name of the page
	 * @param version  The version to check
	 * @return <code>true</code> if the page exists, <code>false</code> otherwise
	 * @throws ProviderException если что-то пошло не так, как ожидалось.
	 */
	boolean pageExists(String pageName, int version) throws ProviderException;

	/**
	 * Checks for existence of a specific page and version denoted by a WikiPage on the underlying
	 * WikiPageProvider.
	 *
	 * @param page A WikiPage object describing the name and version.
	 * @return true, if the page (or alias, or attachment) exists.
	 * @throws ProviderException если что-то пошло не так, как ожидалось.
	 */
	default boolean pageExists(final WikiPage page) throws ProviderException {
		if (page != null) {
			return pageExistsById(page.getId());
		}
		return false;
	}

	/**
	 * Returns true, if the requested page exists. Will consider any version as existing.
	 *
	 * @param pageId ID of the page.
	 * @return true, if page exists.
	 */
	boolean pageExistsById(String pageId);

	/**
	 * Returns true, if the requested page (or an alias) exists. Will consider any version as existing.
	 * Will check for all types of WikiPages: wiki pages themselves, attachments and special pages
	 * (non-existant references to other pages).
	 *
	 * @param page WikiName of the page.
	 * @return true, if page (or attachment) exists.
	 */
	boolean pageExistsByName(String page);

	/**
	 * Returns true, if the requested page (or an alias) exists with the requested version. Will check
	 * for all types of WikiPages: wiki pages themselves, attachments and special pages (non-existant
	 * references to other pages).
	 *
	 * @param page    Page name
	 * @param version Page version
	 * @return True, if page (or alias, or attachment) exists
	 * @throws ProviderException если что-то пошло не так, как ожидалось.
	 */
	boolean wikiPageExists(String page, int version) throws ProviderException;

	/**
	 * Returns true, if the requested page (or an alias) exists, with the specified version in the
	 * WikiPage. Will check for all types of WikiPages: wiki pages themselves, attachments and special
	 * pages (non-existant references to other pages).
	 *
	 * @param page A WikiPage object describing the name and version.
	 * @return true, if the page (or alias, or attachment) exists.
	 * @throws ProviderException если что-то пошло не так, как ожидалось.
	 */
	default boolean wikiPageExists(final WikiPage page) throws ProviderException {
		if (page != null) {
			return wikiPageExists(page.getName(), page.getLastVersion());
		}
		return false;
	}

	/**
	 * Deletes only a specific version of a WikiPage.
	 *
	 * @param page The page to delete.
	 * @throws ProviderException если что-то пошло не так, как ожидалось.
	 */
	void deleteVersion(WikiPage page) throws ProviderException;

	/**
	 * Deletes a page or an attachment completely, including all versions. If the page does not exist,
	 * does nothing.
	 *
	 * @param pageName The name of the page.
	 * @throws ProviderException если что-то пошло не так, как ожидалось.
	 */
	void deletePage(String pageName) throws ProviderException;

	/**
	 * Удаляет всю страницу, все версии контента, прикреплений, все ее следы.
	 *
	 * @param page Вики-страница, которую нужно удалить.
	 * @throws ProviderException если что-то пошло не так, как ожидалось.
	 */
	void deletePage(WikiPage page) throws ProviderException;

	/**
	 * Returns the configured {@link PageSorter}.
	 *
	 * @return the configured {@link PageSorter}.
	 */
	PageSorter getPageSorter();

	/**
	 * Returns the page corresponding to the page ID.
	 *
	 * @param pageId ID of the required page.
	 * @return Wiki page or <code>null</code>.
	 * @throws ProviderException если что-то пошло не так, как ожидалось.
	 */
	WikiPage getPageById(String pageId) throws ProviderException;

	String getMainPageId();

	/**
	 * Creates new wiki page.</br>
	 * If the ID of the parent page is incorrect, then the page is created in the wiki root.
	 *
	 * @param pageName     The name of the new page.
	 * @param parentPageId ID of the parent WikiPage.
	 * @return New wiki page.
	 * @throws ProviderException если что-то пошло не так, как ожидалось.
	 */
	WikiPage createPage(String pageName, String parentPageId) throws ProviderException;

	/**
	 * Add attachment metadata for specisied wiki page.
	 *
	 * @param wikiPage   specified page.
	 * @param attContent new metadata of attachment.
	 * @param attName    name of attachment.
	 * @throws Exception если что-то пошло не так, как ожидалось.
	 */
	void addAttachment(WikiPage wikiPage, AttachmentContent attContent, String attName) throws Exception;

	/**
	 * Returns the PageAttachment corresponding to the page attachment ID.
	 *
	 * @param pageAttachmentId page attachment ID.
	 * @return
	 * @throws Exception если что-то пошло не так, как ожидалось.
	 */
	PageAttachment getPageAttachmentById(String pageAttachmentId) throws Exception;

	void updateReferences(WikiPage page, Collection<String> pagesIds, Collection<String> unknownPages)
			throws ProviderException;

	/**
	 * Finds all references that refer to this page. Returns a collection containing
	 * {@link PageReference} that refer to this one.</br>
	 * The page version is not taken into account.
	 * <p>
	 *
	 * @param pageId The page ID to find referrers for.
	 * @return A List of {@link PageReference}. May return empty, if it has no references.
	 * @throws WikiException если что-то пошло не так, как ожидалось.
	 */
	List<PageReference> getPageReferrers(String pageId) throws WikiException;

	/**
	 * Returns a collection of pages containing links to Undefined Pages (pages containing dead links).
	 *
	 * @return Collection of pages containing links to Undefined Pages.
	 * @throws ProviderException если что-то пошло не так, как ожидалось.
	 */
	Collection<WikiPage> getReferrersToUncreatedPages() throws ProviderException;

	/**
	 * Returns a collection of names of pages that do not exist in the wiki.
	 *
	 * @return Collection of names of pages that do not exist in the wiki.
	 * @throws ProviderException если что-то пошло не так, как ожидалось.
	 */
	Collection<UnknownPage> getUnknownPages() throws ProviderException;

	/**
	 * Returns a collection of pages that are not linked to in other pages.
	 *
	 * @return Collection of pages that are not linked to in other pages.
	 */
	Collection<WikiPage> getUnreferencedPages() throws ProviderException;

	/**
	 * Moves the page.
	 *
	 * @param motionType
	 * @param targetPageId
	 * @param movedPageId
	 * @throws ProviderException если что-то пошло не так, как ожидалось.
	 */
	void movePage(PageMotionType motionType, String targetPageId, String movedPageId) throws ProviderException;

	void renamePage(String newName, WikiPage renamedPage);

}
